﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __SERVER_MAP_INL__
#define __SERVER_MAP_INL__

#pragma once

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_INFO : 服务器信息结构定义
INLINE tagSERVER_INFO::tagSERVER_INFO(void)
{
    Reset();
}

INLINE tagSERVER_INFO::~tagSERVER_INFO(void)
{
    usStatus = STATUSU_NONE;
}

INLINE void tagSERVER_INFO::Info(CStream& Stream, UShort us)
{
    if (Stream.IsRead())
    {
        Stream >> uTotal >> uOnline >> usLoad >> usIncr >> usId >> usStatus;
    }
    else
    {
        Stream << uTotal << uOnline << usLoad << usIncr << usId;
        ((us & STATUSU_OKAY) == 0) ? (Stream << usStatus) : (Stream << (UShort)STATUSU_LINK);
    }
}

INLINE void tagSERVER_INFO::Copy(const tagSERVER_STATUS& ss)
{
    uTotal    = ss.uTotal;
    uOnline   = ss.uOnline;
    usLoad    = ss.usLoad;
    usIncr    = ss.usIncr;
    usId      = ss.usId;
    usStatus  = ss.usStatus;
    if (usStatus != STATUSU_LINK)
    {
        usStatus  = STATUSU_SYNC;
    }
}

INLINE void tagSERVER_INFO::Incr(const tagSERVER_STATUS& ss)
{
    CAtomics::Add<UInt>(&uTotal,  ss.uTotal);
    CAtomics::Add<UInt>(&uOnline, ss.uOnline);
    CAtomics::Increment16<Short>((PShort)&usLoad); // 表示同类型服务器数量增加
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::Diff(const tagSERVER_STATUS& ssIncr, const tagSERVER_STATUS& ssDecr)
{
    CAtomics::Add<UInt>(&uTotal, ssIncr.uTotal);
    if (CAtomics::CompareExchange<UInt>(&uTotal, 0, 0) > ssDecr.uTotal)
    {
        CAtomics::Exchange<UInt>(&uTotal, (uTotal - ssDecr.uTotal));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uTotal, 0);
    }
    CAtomics::Add<UInt>(&uOnline, ssIncr.uOnline);
    if (CAtomics::CompareExchange<UInt>(&uOnline, 0, 0) > ssDecr.uOnline)
    {
        CAtomics::Exchange<UInt>(&uOnline, (uOnline - ssDecr.uOnline));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uOnline, 0);
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::Decr(const tagSERVER_STATUS& ss)
{
    if (CAtomics::CompareExchange<UInt>(&uTotal, 0, 0) > ss.uTotal)
    {
        CAtomics::Exchange<UInt>(&uTotal, (uTotal - ss.uTotal));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uTotal, 0);
    }
    if (CAtomics::CompareExchange<UInt>(&uOnline, 0, 0) > ss.uOnline)
    {
        CAtomics::Exchange<UInt>(&uOnline, (uOnline - ss.uOnline));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uOnline, 0);
    }
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) > 0)
    {
        CAtomics::Decrement16<Short>((PShort)&usLoad); // 表示同类型服务器数量减少
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::IncrTotal(bool bLoad, UInt uIncr)
{
    CAtomics::Add<UInt>(&uTotal, uIncr);
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)(DATAD_OVER_LOAD - usIncr)))
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad + usIncr));
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::DecrTotal(bool bLoad, UInt uDecr)
{
    if (CAtomics::CompareExchange<UInt>(&uTotal, 0, 0) > uDecr)
    {
        CAtomics::Exchange<UInt>(&uTotal, (uTotal - uDecr));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uTotal, 0);
    }
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_OVER_LOAD))
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) > usIncr)
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad - usIncr));
        }
        else
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, 0);
        }
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::IncrOnline(bool bLoad, UInt uIncr)
{
    CAtomics::Add<UInt>(&uOnline, uIncr);
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)(DATAD_OVER_LOAD - usIncr)))
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad + usIncr));
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::DecrOnline(bool bLoad, UInt uDecr)
{
    if (CAtomics::CompareExchange<UInt>(&uOnline, 0, 0) > uDecr)
    {
        CAtomics::Exchange<UInt>(&uOnline, (uOnline - uDecr));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uOnline, 0);
    }
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_OVER_LOAD))
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) > usIncr)
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad - usIncr));
        }
        else
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, 0);
        }
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::Zero(void)
{
    uTotal   = 0;
    uOnline  = 0;
    usLoad   = 0;
    usStatus = STATUSU_NONE;
}

INLINE void tagSERVER_INFO::Reset(void)
{
    MM_SAFE::Set(this, 0, sizeof(tagSERVER_INFO));
}

INLINE void tagSERVER_INFO::SetUpdate(void)
{
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::ClearOnline(bool bTotal)
{
    CAtomics::Exchange<UInt>(&uOnline, 0);
    if (bTotal)
    {
        CAtomics::Exchange<UInt>(&uTotal, 0);
    }
}

INLINE void tagSERVER_INFO::Clear(bool bTotal)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) <= (UShort)DATAD_WARN_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, bTotal ? (Short)(UShort)(uTotal * usIncr) : (Short)(UShort)(uOnline * usIncr));
    }
    else if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_MAY_DAY)
    {
        CAtomics::Decrement16<Short>((PShort)&usLoad);
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::ClearLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) <= (UShort)DATAD_WARN_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, 0);
        usStatus = STATUSU_SYNC;
    }
}

INLINE void tagSERVER_INFO::BusyLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_BUSY_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)DATAD_BUSY_LOAD);
        usStatus = STATUSU_SYNC;
    }
}

INLINE void tagSERVER_INFO::FullLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_FULL_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)DATAD_FULL_LOAD);
        usStatus = STATUSU_SYNC;
    }
}

INLINE void tagSERVER_INFO::OverLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_OVER_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)DATAD_OVER_LOAD);
        usStatus = STATUSU_SYNC;
    }
}

INLINE void tagSERVER_INFO::WarnLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_WARN_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)DATAD_WARN_LOAD);
    }
    else
    {
        CAtomics::Increment16<Short>((PShort)&usLoad);
    }
    usStatus = STATUSU_SYNC;
}

INLINE void tagSERVER_INFO::MayDay(void)
{
    CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)DATAD_MAY_DAY);
    usStatus = STATUSU_SYNC;
}

INLINE bool tagSERVER_INFO::IsBusyLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_FULL_LOAD)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_BUSY_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_BUSY_LOAD);
}

INLINE bool tagSERVER_INFO::IsFullLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_OVER_LOAD)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_FULL_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_FULL_LOAD);
}

INLINE bool tagSERVER_INFO::IsOverLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_WARN_LOAD)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_OVER_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_OVER_LOAD);
}

INLINE bool tagSERVER_INFO::IsWarnLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)DATAD_MAY_DAY)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_WARN_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_WARN_LOAD);
}

INLINE bool tagSERVER_INFO::IsMayDay(void)
{
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)DATAD_MAY_DAY);
}

INLINE bool tagSERVER_INFO::IsUpdated(void)
{
    return (usStatus != STATUSU_NONE);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_NOADDR : 不包含地址的服务器信息结构定义
INLINE tagSERVER_NOADDR::tagSERVER_NOADDR(void)
{
}

INLINE tagSERVER_NOADDR::~tagSERVER_NOADDR(void)
{
}

INLINE Int tagSERVER_NOADDR::Count(void)
{
    return 0;
}

INLINE size_t tagSERVER_NOADDR::Length(void)
{
    return sizeof(SERVER_INFO);
}

INLINE size_t tagSERVER_NOADDR::AddrLength(void)
{
    return 0;
}

INLINE void tagSERVER_NOADDR::Addr(CStream&, UShort)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_ADDR : 包含地址的服务器信息结构定义
template <size_t stCount>
INLINE tagSERVER_ADDR<stCount>::tagSERVER_ADDR(void)
{
}

template <size_t stCount>
INLINE tagSERVER_ADDR<stCount>::~tagSERVER_ADDR(void)
{
}

template <size_t stCount>
INLINE Int tagSERVER_ADDR<stCount>::Count(void)
{
    return (Int)stCount;
}

template <size_t stCount>
INLINE size_t tagSERVER_ADDR<stCount>::Length(void)
{
    return (sizeof(SERVER_INFO) + stCount * sizeof(CNetAddr));
}

template <size_t stCount>
INLINE size_t tagSERVER_ADDR<stCount>::AddrLength(void)
{
    size_t stLength = 0;
    for (size_t i = 0; i < stCount; ++i)
    {
        stLength += NetAddr[i].Length();
    }
    return stLength;
}

template <size_t stCount>
INLINE void tagSERVER_ADDR<stCount>::Addr(CStream& Stream, UShort us)
{
    if ((us & STATUSU_NOADDR) == 0)
    {
        for (size_t i = 0; i < stCount; ++i)
        {
            NetAddr[i].Serialize(Stream);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_MAP::tagSVR_PAIR
template <typename V>
INLINE void tagSERVER_MAP<V>::tagSVR_PAIR::Serialize(CStream& Stream)
{
    Stream.IsRead() ? (Stream >> ullKey) : (Stream << ullKey);
    Value.Info(Stream);
    if (Value.usStatus == STATUSU_LINK)
    {
        Value.Addr(Stream);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_MAP : 服务器信息映射表结构定义
template <typename V>
INLINE void tagSERVER_MAP<V>::Update(void)
{
    for (PINDEX index = SvrMap.GetFirstIndex(); index != nullptr;)
    {
        SVR_MAP_PAIR* pPair = SvrMap.GetNext(index);
        if (pPair->m_V.usStatus & (STATUSU_SYNC | STATUSU_LINK))
        {
            pPair->m_V.usStatus = STATUSU_OKAY;
        }
        else if (pPair->m_V.usStatus & STATUSU_UNLINK)
        {
            SvrMap.RemoveAt(reinterpret_cast<PINDEX>(pPair));
        }
    }
}

template <typename V>
INLINE bool tagSERVER_MAP<V>::Read(CStream& Stream, UShort us)
{
    bool bRet = false;
    if (Stream.IsRead())
    {
        V vVal;
        ULLong ullKey = 0;
        Stream >> ullKey;
        vVal.Info(Stream, us);

        PINDEX index = SvrMap.FindIndex(ullKey);
        switch (vVal.usStatus)
        {
        case STATUSU_SYNC:
            {
                if (index != nullptr)
                {
                    if (us & STATUSU_UPDATE)
                    {
                        vVal.usStatus = STATUSU_OKAY;
                    }
                    SvrMap[index].Copy(vVal);
                    bRet = true;
                    if (us & STATUSU_DUMP)
                    {
                        DEV_DUMP(TF("Read %X status sync %p"), us, ullKey);
                    }
                }
                else if (us & STATUSU_DUMP)
                {
                    DEV_DUMP(TF("Read %X status sync %p failed"), us, ullKey);
                }
            }
            break;
        case STATUSU_LINK:
            {
                vVal.Addr(Stream, us);
                if (index == nullptr)
                {
                    if (us & STATUSU_UPDATE)
                    {
                        vVal.usStatus = STATUSU_OKAY;
                    }
                    SvrMap.Add(ullKey, vVal);
                    bRet = true;
                    if (us & STATUSU_DUMP)
                    {
                        DEV_DUMP(TF("Read %X status link %p"), us, ullKey);
                    }
                }
                else if (us & STATUSU_DUMP)
                {
                    DEV_DUMP(TF("Read %X status link %p failed"), us, ullKey);
                }
            }
            break;
        case STATUSU_UNLINK:
            {
                if (index != nullptr)
                {
                    if (us & STATUSU_UPDATE)
                    {
                        SvrMap.RemoveAt(index);
                    }
                    else
                    {
                        SvrMap[index].usStatus = STATUSU_UNLINK;
                    }
                    bRet = true;
                    if (us & STATUSU_DUMP)
                    {
                        DEV_DUMP(TF("Read %X status unlink %p"), us, ullKey);
                    }
                }
                else if (us & STATUSU_DUMP)
                {
                    DEV_DUMP(TF("Read %X status unlink %p failed"), us, ullKey);
                }
            }
            break;
        default:
            {
                DEV_WARN(TF("Read %X status unknown %p"), us, ullKey);
            }
        }
    }
    return bRet;
}

template <typename V>
INLINE bool tagSERVER_MAP<V>::Write(CStream& Stream, SVR_MAP_PAIR* pPair, UShort us)
{
    if ((pPair != nullptr) && (pPair->m_V.usStatus & us))
    {
        Stream << pPair->m_K;
        if (us & STATUSU_OKAY)
        {
            // 发送同步+完成的服务器信息
            pPair->m_V.Info(Stream, us);
            pPair->m_V.Addr(Stream, us);
            if (us & STATUSU_DUMP)
            {
                DEV_DUMP(TF("Write %X status sync & okay %p"), us, pPair->m_K);
            }
        }
        else
        {
            switch (pPair->m_V.usStatus)
            {
            case STATUSU_SYNC:
            case STATUSU_UNLINK:
                {
                    pPair->m_V.Info(Stream, us);
                    if (us & STATUSU_DUMP)
                    {
                        DEV_DUMP(TF("Write %X status sync & unlink %p"), us, pPair->m_K);
                    }
                }
                break;
            case STATUSU_LINK:
                {
                    pPair->m_V.Info(Stream, us);
                    pPair->m_V.Addr(Stream, us);
                    if (us & STATUSU_DUMP)
                    {
                        DEV_DUMP(TF("Write %X status link %p"), us, pPair->m_K);
                    }
                }
                break;
            default:
                {
                    DEV_WARN(TF("Write %X status unknown %p"), us, pPair->m_K);
                }
            }
        }
        return true;
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 服务器信息交换链定义
template <typename TCHAIN>
INLINE tagSERVER_CHAIN<TCHAIN>::tagSERVER_CHAIN(void)
: uIndex(0)
{
}

template <typename TCHAIN>
INLINE tagSERVER_CHAIN<TCHAIN>::~tagSERVER_CHAIN(void)
{
}

template <typename TCHAIN>
INLINE void tagSERVER_CHAIN<TCHAIN>::Init(Int nHashSize)
{
    uIndex = 0;
    Chain[0].SetHash(nHashSize);
    Chain[1].SetHash(nHashSize);
}

template <typename TCHAIN>
INLINE void tagSERVER_CHAIN<TCHAIN>::Exit(void)
{
    Chain[0].RemoveAll();
    Chain[1].RemoveAll();
}

template <typename TCHAIN>
INLINE TCHAIN& tagSERVER_CHAIN<TCHAIN>::GetCurr(void)
{
    return Chain[CAtomics::CompareExchange<UInt>(&uIndex, 0, 0)];
}

template <typename TCHAIN>
INLINE TCHAIN& tagSERVER_CHAIN<TCHAIN>::GetNext(void)
{
    return Chain[(CAtomics::CompareExchange<UInt>(&uIndex, 0, 0) != 0) ? 0 : 1];
}

template <typename TCHAIN>
INLINE TCHAIN& tagSERVER_CHAIN<TCHAIN>::GetNextClone(void)
{
    UInt uNext = (CAtomics::CompareExchange<UInt>(&uIndex, 0, 0) != 0) ? 0 : 1;
    Chain[uNext] = Chain[uIndex];
    return Chain[uNext];
}

template <typename TCHAIN>
INLINE void tagSERVER_CHAIN<TCHAIN>::Swap(void)
{
    if (CAtomics::CompareExchange<UInt>(&uIndex, 1, 0) != 0)
    {
        CAtomics::Decrement<UInt>(&uIndex);
    }
}


#endif // __SERVER_MAP_INL__
